package com.mouse.damo.micro;

import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.Variant;
import com.mouse.damo.ui.FileUtil;
import com.mouse.damo.ui.Lang;
import com.mouse.damo.ui.Log;
import com.mouse.damo.util.VarUtil;
import lombok.Getter;
import lombok.Setter;

import java.io.File;
import java.io.FileInputStream;


/**
 * 辅助时必须先创建此类，获取此类的 {@link Com#activeDm} 对象，所有的都对此对象操作(绑定后为后台操作，不绑定直接获取的话是前台操作)
 * 一个Com对象只能绑定一个窗口
 */
public class Com {
    Log log;
    private String jdkPath = Lang.getCurrentJrePath();
    public boolean initDll = false;//是否已经初始化DLL过了，项目创建时实例化过一次后变为true，以后再有实例不会再初始化dll
    @Getter
    private boolean createSuccess = false;    //创建Com成功 调用getCreateSuccess()返回结果，证明创建成功
    @Getter
    private ActiveXComponent activeDm;    //大漠插件对象
    @Setter
    @Getter
    private int hwnd = 0;//已绑定的句柄

    public Com() {
        initCheckJdk();
        initJacobDll();
        ComThread.InitSTA();
        log = new Log();
        //注册，创建dm.dmsoft对象
        createObject();
        setShowErrorMsg(0);
    }

    /**
     * 需要是Java32位
     */
    private void initCheckJdk() {
        String arch = System.getProperty("os.arch");
        if (arch.indexOf("64") == -1) {
            //通过
            this.createSuccess = true;
        } else {
            this.createSuccess = true;
            System.err.println("运行请使用 32位的Java 版本！下载地址： http://www.xnx3.com/doc/jre1.7.html");
        }
    }

    /**
     * Dll文件初始化，将对应的dll复制到相应的文件夹内
     */
    private void initJacobDll() {
        if (initDll || !createSuccess) {
            return;
        }
        try {
            String jacobX64 = jdkPath + "\\bin\\jacob-1.18-x64.dll";
            String jacobX86 = jdkPath + "\\bin\\jacob-1.18-x86.dll";
            if (!FileUtil.exists(jacobX64) || !FileUtil.exists(jacobX86)) {
                this.createSuccess = false;
                FileUtil.inputStreamToFile(new FileInputStream(new File("./dll/jacob-1.18-x64.dll")), jacobX64);
                FileUtil.inputStreamToFile(new FileInputStream(new File("./dll/jacob-1.18-x86.dll")), jacobX86);
                System.out.println("系统缺少jacob-1.18-x64.dll、jacob-1.18-x86.dll文件，已自动修复！");
                this.createSuccess = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建dm.dmsoft对象
     * <li>检测dm.dll是否已注册,若是没有注册，则自动注册
     * <li>若是已注册，则创建dm.dmsoft对象
     */
    private void createObject() {
        if (initDll) {
            return;
        }
        initDll = true;
        try {
            activeDm = new ActiveXComponent("dm.dmsoft");//创建大漠对象
            this.createSuccess = true;
        } catch (Exception e) {
            e.printStackTrace();
            if (e.getMessage().equals("Can't co-create object")) {
                System.out.println("dll插件未注册.请用超级管理员打开文件doc/register.bat");
            }
        }

    }


    /**
     * 窗口绑定，绑定后可任意操作该窗口
     *
     * @param hwnd 要绑定的窗口句柄  获取方式位于： {@link Window#findWindow(int, String, String)} {@link Window#getMousePointWindowHwnd()}
     * @return boolean <li>true:绑定成功
     * <li>false:绑定失败
     */
    public boolean bind(int hwnd) {
        return bind(hwnd, "dx2", "windows3", "windows", 0, true);
    }

    public boolean bindHead(int hwnd) {
        return bind(hwnd, "dx2", "windows3", "windows", 0, false);
    }

    /**
     * 窗口绑定， 绑定后可任意操作该窗口，每个程序都不一样，如果鼠标、键盘、截图、取色等不好使，需要多试试几种组合，以便找到其最佳组合
     *
     * @param hwnd    要绑定的窗口句柄  获取方式位于： {@link Window#findWindow(int, String, String)} {@link Window#getMousePointWindowHwnd()}
     * @param display 屏幕颜色获取方式 取值有以下几种
     *                <ul>
     *                <li>normal正常模式,平常我们用的前台截屏模式
     *                <li>gdi模式,用于窗口采用GDI方式刷新时. 此模式占用CPU较大.
     *                <li>gdi2模式,此模式兼容性较强,但是速度比gdi模式要慢许多,如果gdi模式发现后台不刷新时,可以考虑用gdi2模式
     *                <li>dx图像模式 dx.graphic.2d|dx.graphic.3d .<b>注意此模式需要管理员权限</b>
     *                </ul>
     * @param mouse   鼠标仿真模式 取值有以下几种
     *                <ul>
     *                <li>normal正常模式,平常我们用的前台鼠标模式
     *                <li>windows模式,采取模拟windows消息方式 同按键自带后台插件
     *                <li>windows3模式，采取模拟windows消息方式,可以支持有多个子窗口的窗口后台
     *                <li>dx模式,采用模拟dx后台鼠标模式,这种方式会锁定鼠标输入.有些窗口在此模式下绑定时，需要先激活窗口再绑定(或者绑定以后激活)，否则可能会出现绑定后鼠标无效的情况.<b>注意此模式需要管理员权限</b>
     *                </ul>
     * @param key     键盘仿真模式 取值有以下几种
     *                <ul>
     *                <li>normal正常模式,平常我们用的前台键盘模式
     *                <li>windows模式,采取模拟windows消息方式 同按键精灵的后台插件
     *                <li>dx模式,采用模拟dx后台键盘模式。有些窗口在此模式下绑定时，需要先激活窗口再绑定(或者绑定以后激活)，否则可能会出现绑定后键盘无效的情况.<b>注意此模式需要管理员权限</b>
     *                </ul>
     * @param mode    窗口绑定的模式
     *                <ul>
     *                <li>0 : 推荐模式此模式比较通用，而且后台效果是最好的.
     *                <li>2 : 同模式0,此模式为老的模式0,尽量不要用此模式，除非有兼容性问题
     *                <li>4 : 同模式0,如果模式0有崩溃问题，可以尝试此模式.
     * @return boolean 窗口绑定结果
     * <ul>
     * <li>true:绑定成功
     * <li>false:绑定失败
     * </ul>
     */
    public boolean bind(int hwnd, String display, String mouse, String key, int mode, boolean back) {
        boolean xnx3_result = false;
        for (int i = 0; i < 30; i++) {
            try {
                //激活窗口
                if (back) {
//                    new Window(this).setWindowState(hwnd, 5);
                } else {
                    new Window(this).setWindowState(hwnd, 1);
                }
                //进行dm窗口绑定
                Variant[] var = new Variant[5];
                var[0] = new Variant(hwnd);
                var[1] = new Variant(display);
                var[2] = new Variant(mouse);
                var[3] = new Variant(key);
                var[4] = new Variant(mode);
                int bindWindow = activeDm.invoke("BindWindow", var).getInt();
                var = null;
                if (bindWindow == 0) {
                    activeDm.invoke("ForceUnBindWindow", hwnd);
                    xnx3_result = false;
                } else {    //绑定成功
                    xnx3_result = true;
                    this.hwnd = hwnd;
                    break;
                }
            } catch (Exception e) {
                xnx3_result = false;
                e.printStackTrace();
                //log.debug(this, "bind", "异常:"+e.getMessage());
            }
        }
        return xnx3_result;
    }


    /**
     * 解除绑定，释放内存
     * <li>1.解除之前绑定的窗口，若是没有绑定操作的窗口，则自动跳过
     * <li>2.调用 {@link Com#destroy()} 进行资源、内存释放。
     *
     * @return <li>true：解除绑定成功，同时自动释放ActiveXComponent对象，释放资源
     * <li>false:解除绑定失败，若是失败，不做任何处理。游戏运行时间太长一两天的有时候会遇到此种情况
     */
    public boolean unbind() {
        boolean xnx3_result = false;
        //有绑定窗口了，解除绑定窗口
        try {
            //解除绑定前将窗口设置为当前活动窗口
            activeDm.invoke("SetWindowState", hwnd, 1);
            int getResult = activeDm.invoke("UnBindWindow").getInt();
            if (getResult == 0) {    //解除绑定失败，使用强制接触绑定
                if (activeDm.invoke("ForceUnBindWindow", hwnd).getInt() == 1) {
                    xnx3_result = true;
                }
            } else {
                xnx3_result = true;
            }
//				解除对窗口的绑定，进行资源释放
        } catch (Exception e) {
            e.printStackTrace();
            //log.debug(this, "unbind", "异常:"+e.getMessage());
        }
        return xnx3_result;
    }

    /**
     * 销毁，释放内存
     * <li>若是之前调用过 {@link Com#bind(int)} 绑定了操作的目标窗口了，则需要使用 {@link Com#unbind()}  进行释放
     */
    public void destroy() {
        ComThread.Release();
        activeDm = null;
    }

    /**
     * 设置点阵字库
     *
     * @param num      字库的序号,取值为0-9,目前最多支持10个字库，序号设置不可以重复
     * @param fileName 字库文件名，如 mapPositionDict.txt ,此文件须位于设置的 {@link Com#setResourcePath(String)} 资源路径里，也就是资源路径必须于此函数之前先设置好
     * @return boolean true:成功
     */
    public boolean setDict(int num, String fileName) {
        boolean xnx3_result = false;

        Variant[] var = new Variant[2];
        try {
            var[0] = new Variant(num);
            var[1] = new Variant(fileName);
            if (activeDm.invoke("SetDict", var).getInt() == 1) {
                xnx3_result = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            //log.debug(this, "setDict", "异常:"+e.getMessage());
        } finally {
            var = null;
        }
        return xnx3_result;
    }
    public boolean clearDict(int index) {
        //设置当前资源路径
        int setPath = activeDm.invoke("ClearDict", index).getInt();
        return setPath == 1;
    }
    /**
     * 设置资源文件的所在路径，例如找图、点阵字库等文件所放的图片路径，统一放到此文件夹内
     *
     * @param resourcePath 资源路径文件夹，如： F:/waigua/resource
     * @return boolean
     */
    public boolean setResourcePath(String resourcePath) {
        //设置当前资源路径
        int setPath = activeDm.invoke("SetPath", resourcePath).getInt();
        return setPath == 1;
    }

    /**
     * 设置是否弹出错误信息,默认是打开.
     *
     * @param show 整形数: 0表示不打开,1表示打开
     * @return boolean
     */
    public boolean setShowErrorMsg(int show) {
        //设置当前资源路径
        int result = activeDm.invoke("SetShowErrorMsg", show).getInt();
        return result == 1;
    }

    /**
     * 鼠标动作模拟真实操作,带移动轨迹,以及点击延时随机.
     *
     * @param enable 0 关闭模拟 1 开启模拟
     * @return
     */
    public boolean enableRealMouse(int enable) {
        //设置当前资源路径
        Variant[] vars = VarUtil.getVars(enable, 30, 30);
        int result = activeDm.invoke("EnableRealMouse", vars).getInt();
        return result == 1;
    }


}